import { getDB, initSQLite } from './sqlite_manager.js'
import fs from 'fs/promises'
import Ajv from 'ajv'
import path from 'path'
import { pluginRoot } from './common-lib/paths.js'

const locks = new Map()
function withLock (key, fn) {
  const prev = locks.get(key) || Promise.resolve()
  const next = prev.then(() => fn()).catch(err => { throw err })
  locks.set(key, next.finally(() => { if (locks.get(key) === next) locks.delete(key) }))
  return next
}

initSQLite()
const db = getDB()

// 载入并编译 JSON Schema（用于读写时校验）
const ajv = new Ajv({ allErrors: true, strict: false })
const validators = new Map()

async function loadSchema (name, filename) {
  try {
    const full = path.join(pluginRoot, 'data', 'schemas', filename)
    const txt = await fs.readFile(full, 'utf-8')
    const json = JSON.parse(txt)
    validators.set(name, ajv.compile(json))
  } catch {}
}

// 初始化加载可用的 Schema
await Promise.all([
  loadSchema('user', 'user.json'),
  loadSchema('inventory_item', 'inventory_item.json'),
  loadSchema('workshop', 'workshop.json'),
  loadSchema('synthesis_record', 'synthesis_record.json'),
  loadSchema('relationship', 'relationship.json'),
  loadSchema('signin', 'signin.json'),
  loadSchema('boost', 'boost.json'),
  loadSchema('quest', 'quest.json')
])

function validateOrThrow (name, data) {
  const v = validators.get(name)
  if (!v) return true
  const ok = v(data)
  if (!ok) {
    const msg = ajv.errorsText(v.errors, { separator: '; ' })
    throw new Error(`Schema validate failed for ${name}: ${msg}`)
  }
  return true
}

export async function getUserHome (userId, groupId) {
  const row = db.prepare('SELECT money,love,level FROM users WHERE user_id=? AND group_id=?').get(String(userId), String(groupId || 'global'))
  if (!row) return { money: 0, love: 0, level: 0 }
  return row
}

export async function saveUserHome (userId, groupId, data) {
  const key = `users:${groupId}:${userId}`
  return withLock(key, async () => {
    // 校验数据
    validateOrThrow('user', { money: Number(data.money || 0), love: Number(data.love || 0), level: Number(data.level || 0) })
    db.prepare('INSERT INTO users (user_id,group_id,money,love,level) VALUES (?,?,?,?,?) ON CONFLICT(user_id,group_id) DO UPDATE SET money=excluded.money,love=excluded.love,level=excluded.level')
      .run(String(userId), String(groupId || 'global'), Number(data.money || 0), Number(data.love || 0), Number(data.level || 0))
    return true
  })
}

export async function getUserBattle (userId, groupId) {
  const row = db.prepare('SELECT level,attack,defense,speed FROM battle WHERE user_id=? AND group_id=?').get(String(userId), String(groupId || 'global'))
  if (!row) return { level: 0, attack: 0, defense: 0, speed: 0 }
  return row
}

export async function saveUserBattle (userId, groupId, data) {
  const key = `battle:${groupId}:${userId}`
  return withLock(key, async () => {
    db.prepare('INSERT INTO battle (user_id,group_id,level,attack,defense,speed) VALUES (?,?,?,?,?,?) ON CONFLICT(user_id,group_id) DO UPDATE SET level=excluded.level,attack=excluded.attack,defense=excluded.defense,speed=excluded.speed')
      .run(String(userId), String(groupId || 'global'), Number(data.level || 0), Number(data.attack || 0), Number(data.defense || 0), Number(data.speed || 0))
    return true
  })
}

export async function loadUserInventory (userId, groupId) {
  const rows = db.prepare('SELECT item_id,item_json FROM inventory WHERE user_id=? AND group_id=?').all(String(userId), String(groupId || 'global'))
  return rows.map(r => ({ id: r.item_id, ...JSON.parse(r.item_json) }))
}

export async function saveUserInventory (userId, groupId, items) {
  const key = `inventory:${groupId}:${userId}`
  return withLock(key, async () => {
    const del = db.prepare('DELETE FROM inventory WHERE user_id=? AND group_id=?')
    del.run(String(userId), String(groupId || 'global'))
    const ins = db.prepare('INSERT INTO inventory (user_id,group_id,item_id,item_json,ts) VALUES (?,?,?,?,?)')
    const ts = Math.floor(Date.now() / 1000)
    const tx = db.transaction((list) => {
      for (const it of list) {
        const id = String(it.id || it.item_id || Math.random().toString(36).slice(2))
        // 校验物品项
        try { validateOrThrow('inventory_item', it) } catch {}
        ins.run(String(userId), String(groupId || 'global'), id, JSON.stringify(it), ts)
      }
    })
    tx(items || [])
    return true
  })
}

export async function getSignIn (userId, groupId) {
  const row = db.prepare('SELECT streak,last_signed FROM signin WHERE user_id=? AND group_id=?').get(String(userId), String(groupId || 'global'))
  if (!row) return { streak: 0, todaySigned: false }
  const today = new Date().toLocaleDateString()
  return { streak: Number(row.streak || 0), todaySigned: row.last_signed === today }
}

export async function setSignIn (userId, groupId, streak) {
  const key = `signin:${groupId}:${userId}`
  return withLock(key, async () => {
    const today = new Date().toLocaleDateString()
    validateOrThrow('signin', { streak: Number(streak || 0), lastSigned: today })
    db.prepare('INSERT INTO signin (user_id,group_id,streak,last_signed) VALUES (?,?,?,?) ON CONFLICT(user_id,group_id) DO UPDATE SET streak=excluded.streak,last_signed=excluded.last_signed')
      .run(String(userId), String(groupId || 'global'), Number(streak || 0), today)
    return true
  })
}

export async function getBoost (userId, groupId, type) {
  const row = db.prepare('SELECT value FROM boosts WHERE user_id=? AND group_id=? AND type=?').get(String(userId), String(groupId || 'global'), String(type))
  return Number(row?.value || 0)
}

export async function setBoost (userId, groupId, type, value) {
  const key = `boost:${type}:${groupId}:${userId}`
  return withLock(key, async () => {
    validateOrThrow('boost', { type: String(type), value: Number(value || 0) })
    db.prepare('INSERT INTO boosts (user_id,group_id,type,value) VALUES (?,?,?,?) ON CONFLICT(user_id,group_id,type) DO UPDATE SET value=excluded.value')
      .run(String(userId), String(groupId || 'global'), String(type), Number(value || 0))
    return true
  })
}

export async function consumeBoost (userId, groupId, type, amount = 1) {
  const key = `boost:${type}:${groupId}:${userId}`
  return withLock(key, async () => {
    const cur = await getBoost(userId, groupId, type)
    if (cur <= 0) return false
    await setBoost(userId, groupId, type, Math.max(0, cur - amount))
    return true
  })
}

export async function getWorkshop (userId, groupId) {
  const row = db.prepare('SELECT level,slots,data_json FROM workshop WHERE user_id=? AND group_id=?').get(String(userId), String(groupId || 'global'))
  if (!row) return { level: 1, slots: 1 }
  const extra = row.data_json ? JSON.parse(row.data_json) : {}
  return { level: Number(row.level || 1), slots: Number(row.slots || 1), ...extra }
}

export async function saveWorkshop (userId, groupId, data) {
  const key = `workshop:${groupId}:${userId}`
  return withLock(key, async () => {
    validateOrThrow('workshop', { level: Number(data.level || 1), slots: Number(data.slots || 1), data })
    db.prepare('INSERT INTO workshop (user_id,group_id,level,slots,data_json) VALUES (?,?,?,?,?) ON CONFLICT(user_id,group_id) DO UPDATE SET level=excluded.level,slots=excluded.slots,data_json=excluded.data_json')
      .run(String(userId), String(groupId || 'global'), Number(data.level || 1), Number(data.slots || 1), JSON.stringify(data))
    return true
  })
}

export async function getSynthesisHistory (userId, groupId) {
  const rows = db.prepare('SELECT ts,record_json FROM synthesis_history WHERE user_id=? AND group_id=? ORDER BY ts ASC').all(String(userId), String(groupId || 'global'))
  return rows.map(r => ({ ts: Number(r.ts), ...JSON.parse(r.record_json) }))
}

export async function saveSynthesisHistory (userId, groupId, list) {
  const key = `synthesis:${groupId}:${userId}`
  return withLock(key, async () => {
    const del = db.prepare('DELETE FROM synthesis_history WHERE user_id=? AND group_id=?')
    del.run(String(userId), String(groupId || 'global'))
    const ins = db.prepare('INSERT INTO synthesis_history (user_id,group_id,ts,record_json) VALUES (?,?,?,?)')
    const tx = db.transaction((records) => {
      for (const rec of records) ins.run(String(userId), String(groupId || 'global'), Number(rec.ts || Date.now()), JSON.stringify(rec))
    })
    tx(list || [])
    return true
  })
}

export async function recordSynthesis (userId, groupId, record) {
  try { validateOrThrow('synthesis_record', record) } catch {}
  const ins = db.prepare('INSERT INTO synthesis_history (user_id,group_id,ts,record_json) VALUES (?,?,?,?)')
  ins.run(String(userId), String(groupId || 'global'), Number(record.ts || Date.now()), JSON.stringify(record))
  return true
}

export async function getRelationship (userId, groupId) {
  const row = db.prepare('SELECT data_json FROM relationship WHERE user_id=? AND group_id=?').get(String(userId), String(groupId || 'global'))
  return row?.data_json ? JSON.parse(row.data_json) : null
}

export async function setRelationship (userId, groupId, data) {
  const key = `relationship:${groupId}:${userId}`
  return withLock(key, async () => {
    try { validateOrThrow('relationship', data) } catch {}
    db.prepare('INSERT INTO relationship (user_id,group_id,data_json) VALUES (?,?,?) ON CONFLICT(user_id,group_id) DO UPDATE SET data_json=excluded.data_json')
      .run(String(userId), String(groupId || 'global'), JSON.stringify(data))
    return true
  })
}

export async function getQuest (userId, groupId) {
  const row = db.prepare('SELECT data_json FROM quest WHERE user_id=? AND group_id=?').get(String(userId), String(groupId || 'global'))
  return row?.data_json ? JSON.parse(row.data_json) : { points: 0, completed: [], active: [] }
}

export async function setQuest (userId, groupId, data) {
  const key = `quest:${groupId}:${userId}`
  return withLock(key, async () => {
    validateOrThrow('quest', data)
    db.prepare('INSERT INTO quest (user_id,group_id,data_json) VALUES (?,?,?) ON CONFLICT(user_id,group_id) DO UPDATE SET data_json=excluded.data_json')
      .run(String(userId), String(groupId || 'global'), JSON.stringify(data))
    return true
  })
}

export default {
  getUserHome,
  saveUserHome,
  getUserBattle,
  saveUserBattle,
  loadUserInventory,
  saveUserInventory,
  getSignIn,
  setSignIn,
  getBoost,
  setBoost,
  consumeBoost,
  getWorkshop,
  saveWorkshop,
  getSynthesisHistory,
  saveSynthesisHistory,
  recordSynthesis,
  getRelationship,
  setRelationship,
  getQuest,
  setQuest
}